const std = @import("std");

const win = std.os.windows;

// this add: members
allocator: std.mem.Allocator,
arena: std.heap.ArenaAllocator,
// count: *i32 = undefined,

const Screenshotz = @This();

const Point = struct {
    X: i32,
    Y: i32,
};

pub const Rectangle = struct {
    Min: Point,
    Max: Point,
};

/// Initialize a new Screenshotz object
pub fn init(allocator: std.mem.Allocator) Screenshotz {
    return .{
        .allocator = allocator,
        .arena = std.heap.ArenaAllocator.init(allocator),
    };
}

/// Deinitialize and free allocated memory.
pub fn deinit(self: *Screenshotz) void {
    self.arena.deinit();
}

/// Numbers of active displays.
pub fn NumActiveDisplays(self: *Screenshotz) !i32 {
    const allocator = self.arena.allocator();

    const count = try allocator.create(i32);
    // defer allocator.destroy(count);  // 在这里已经包裹起来了，所以不需要[orelse error: memory leaked]

    count.* = 0;
    _ = EnumDisplayMonitors(null, null, countupMonitorCallback, @ptrCast(count));

    return count.*;
}

const getMonitorBoundsContext = struct {
    Index: i32,
    Rect: win.RECT,
    Count: i32,
};

/// Get bounds of display.
pub fn GetDisplayBounds(self: *Screenshotz, display_idx: i32) !Rectangle {
    const allocator = self.arena.allocator();

    const ctx = try allocator.create(getMonitorBoundsContext);
    ctx.Index = display_idx;
    ctx.Count = 0;

    _ = EnumDisplayMonitors(null, null, getMonitorBoundsCallback, @ptrCast(ctx));

    return .{
        .Min = .{.X = 1, .Y = 2},
        .Max = .{.X = 1, .Y = 2},
    };
}

fn getMonitorBoundsCallback(
    hMonitor: ?win.HANDLE,
    _: ?win.HDC,  // hdcMonitor
    _: ?*win.RECT,  // lprcMonitor
    dwData: win.LPVOID
) callconv(win.WINAPI) win.LONG_PTR {
    const ctx: *getMonitorBoundsContext = @alignCast(@ptrCast(dwData));
    if (ctx.Count != ctx.Index) {
        ctx.Count = ctx.Count + 1;
        return 1;
    }

    _ = getMonitorRealSize(hMonitor);

    return 0;
}

const MONITORINFO = struct {
    CbSize: u32,
    RcMonitor: win.RECT,
    RcWork: win.RECT,
    DwFlags: u32,
};

const CCHDEVICENAME = 32;

const _MONITORINFOEX = struct {
    MONITORINFO: MONITORINFO,
    DeviceName: [CCHDEVICENAME]u8,
};

extern "user32" fn GetMonitorInfoA(
    ?win.HANDLE,
    *_MONITORINFOEX
) callconv(win.WINAPI) bool;

fn getMonitorRealSize(
    hMonitor: ?win.HANDLE
) callconv(win.WINAPI) *win.RECT {
    var info: _MONITORINFOEX = undefined;
    info.MONITORINFO.CbSize = @intCast(@sizeOf(_MONITORINFOEX));

    // syscall GetMonitorInfo.
    const ret = GetMonitorInfoA(hMonitor, &info);
    if (ret == false) {
        return undefined;
    }

    return undefined;
}

// extern "user32" fn MessageBoxA(
//         ?win.HWND, 
//         [*:0]const u8, 
//         [*:0]const u8, 
//         u32) callconv(win.WINAPI) i32;

extern "user32" fn EnumDisplayMonitors(
    ?win.HDC, 
    ?*win.RECT, 
    *const fn(?win.HANDLE, ?win.HDC, ?*win.RECT, win.LPVOID) callconv(win.WINAPI) win.LONG_PTR, 
    win.LPVOID
) callconv(win.WINAPI) bool;

fn countupMonitorCallback(
    _ : ?win.HANDLE,  // hMonitor
    _ : ?win.HDC,  // hdcMonitor
    _ : ?*win.RECT,  // lprcMonitor
    dwData: win.LPVOID
) callconv(win.WINAPI) win.LONG_PTR {
    const count: *i32 = @alignCast(@ptrCast(dwData));  // 内存对齐，很有必要
    count.* = count.* + 1;
    return 1;
}
